﻿//-----------------------------------------------------------------------------
// Copyright (c) BaFang Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

namespace BaFangSpider
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Timers;
    using LumiSoft.Net.Mail;

    class WeatherMonitorService : MonitorService
    {
        const string template = @"http://php.weather.sina.com.cn/search.php?city={0}&dpc=1";
        const string zsTemplate = @"http://php.weather.sina.com.cn/search.php?c=1&city={0}&type=zs&dpc=1";

        const string LiTemplate = "<li>";
        const string LiTemplateClose = "</li>";
        const string LiTemplatePlus = "<li class=\"tpte\">";
        const string TemperatureTemplate = "<span class=\"fs_30 tpte\">";
        const string SpanCloseTemplate = "</span>";
        const string Mod03Template = "<div class=\"mod_03\">";
        const string SpanPattern = "<span class=\"fgreen\">|<span class=\"fred\">";

        private DataManager cityAndWeatherDataMan = new DataManager(
            "Weather\\cityandweathers.xml", typeof(SerializableDictionary<string, WeatherReport>));

        private DataManager cityAndInterestsDataMan = new DataManager(
            "Weather\\cityandinterests.xml", typeof(SerializableDictionary<string, string>[]));

        private DataManager timeZoneIndexDataMan = new DataManager(
            "Weather\\timezoneindex.xml", typeof(int));

        private DataManager interestAndZSDataMan = new DataManager(
            "Weather\\interestAndZS.xml", typeof(SerializableDictionary<string, bool>));

        SerializableDictionary<string, WeatherReport> cityAndWeathers;
        SerializableDictionary<string, string>[] interestAndCityArray;
        int timeZoneIndex = -1;
        SerializableDictionary<string, bool> interestAndZS;

        protected Timer getWeatherTimer = new Timer();

        public WeatherMonitorService()
        {
            this.mailAddress = MailAccounts.GetInstance().Weather;
        }

        private void SendReport()
        {
            foreach (KeyValuePair<string, string> pair in this.interestAndCityArray[DateTime.Now.Hour % 24])
            {
                string title = pair.Value + ":" + this.cityAndWeathers[pair.Value].ToString();
                SendMail.Send(pair.Key + "@139.com", title, ConstStrings.UnregisterAlert, true, this.mailAddress);
                if (this.interestAndZS[pair.Key])
                {
                    SendMail.Send(pair.Key + "@139.com", this.cityAndWeathers[pair.Value].ZSToday.ToString(), ConstStrings.UnregisterAlert, true, this.mailAddress);
                }
            }
        }


        // 此处发送天气预报
        protected override void CheckWebSites()
        {
            lock (this.interestAndZS)
            {
                // 为了保证不重复发送。
                if (this.timeZoneIndex != DateTime.Now.Hour)
                {
                    SendReport();
                    timeZoneIndex = DateTime.Now.Hour;
                    this.timeZoneIndexDataMan.SaveData(this.timeZoneIndex);  // Save it to database, so when restart, it wouldn't send report.

                }
            }
        }

        public override void Start()
        {
            base.Start();
            this.checkServiceTimer.Interval = 500000;  // less than 10 minutes.

            getWeatherTimer.AutoReset = true;
            getWeatherTimer.Enabled = true;
            getWeatherTimer.Interval = 12000000; // 
            getWeatherTimer.Elapsed += new ElapsedEventHandler(getWeatherTimer_Elapsed);
            UpdateCityAndWeather();  // 保证每次重启，都能更新一遍。
            interestAndCityArray = cityAndInterestsDataMan.LoadData() as SerializableDictionary<string, string>[];
            if (interestAndCityArray == null)
            {
                interestAndCityArray = new SerializableDictionary<string, string>[24];
            }
            for (int i = 0; i < 24; i++)
            {
                if (interestAndCityArray[i] == null)
                {
                    interestAndCityArray[i] = new SerializableDictionary<string, string>();
                }
            }
            object o = timeZoneIndexDataMan.LoadData();
            if (o != null)
            {
                timeZoneIndex = (int)o;
            }

            this.interestAndZS = interestAndZSDataMan.LoadData() as SerializableDictionary<string, bool>;
            if (this.interestAndZS == null)
            {
                this.interestAndZS = new SerializableDictionary<string, bool>();
            }
        }

        void getWeatherTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            this.UpdateCityAndWeather();
        }

        private WeatherReport GetWeatherReportForCity(string city)
        {
            city = HtmlUtility.EncodeGB2312ToUrl(city);
            string address = string.Format(template, city);
            string html = HtmlUtility.GetHtml(address);
            WeatherReport report = GetWeatherReport(html);

            address = string.Format(zsTemplate, city);
            html = HtmlUtility.GetHtml(address);

            report.ZSToday = GetWeatherZS(ref html);
            report.Today = DateTime.Today;
            return report;
        }

        internal void AddCity(string city)
        {
            if (!cityAndWeathers.ContainsKey(city))
            {
                WeatherReport re = GetWeatherReportForCity(city);
                cityAndWeathers.Add(city, re);
                cityAndWeatherDataMan.SaveData(this.cityAndWeathers);
            }
        }

        protected override void UpdateListenserRegisteredService(string interest, string message)
        {
            lock (message)
            {
                try
                {
                    // Remove original.
                    foreach (Dictionary<string, string> relation in this.interestAndCityArray)
                    {
                        relation.Remove(interest);
                    }

                    this.interestAndZS.Remove(interest);

                    if (!string.IsNullOrEmpty(message))
                    {
                        WeatherService service = ServiceBase.CreateFromString<WeatherService>(message) as WeatherService;
                        foreach (WeatherSerivceEntity entity in service.Entities)
                        {
                            int timeZone = int.Parse(entity.Time);
                            bool shouldSendTimeZone = string.Equals(entity.ShouldSendZS, "Yes", StringComparison.OrdinalIgnoreCase)? true : false;
                            if (interestAndCityArray[timeZone].ContainsKey(interest))
                            {
                                // TODO: resolve this constraints
                                return;
                            }
                            this.interestAndCityArray[timeZone].Add(interest, entity.City);
                            this.cityAndInterestsDataMan.SaveData(interestAndCityArray);
                            if (!this.interestAndZS.ContainsKey(interest))
                            {
                                this.interestAndZS.Add(interest, shouldSendTimeZone);
                            }
                            this.interestAndZSDataMan.SaveData(interestAndZS);
                            AddCity(entity.City);
                        }
   
                    }

                }
                catch (Exception e)
                {
                    Logger.GetLogger().LogException(e);
                }
            }
        }

        void GetCityAndTimeZone(string title, out string city, out int? timeZone, out bool shouldSendZS)
        {
            int next = -1;
            city = this.GetKeywordInMailTitle(title, out next);

            string s = title.Substring(next);
            string time = this.GetKeywordInMailTitle(s, out next);
            timeZone = int.Parse(time);

            s = s.Substring(next);
            string temp = this.GetKeywordInMailTitle(s, out next);
            shouldSendZS = false;
            if (temp != null)
            {
                shouldSendZS = true;
            }
        }

        private void UpdateCityAndWeather()
        {
            try
            {
                this.cityAndWeathers = cityAndWeatherDataMan.LoadData() as SerializableDictionary<string, WeatherReport>;
                if (cityAndWeathers == null)
                {
                    cityAndWeathers = new SerializableDictionary<string, WeatherReport>();
                }
                foreach (string key in cityAndWeathers.Keys)
                {
                    cityAndWeathers[key] = GetWeatherReportForCity(key); // initialize
                }
            }
            catch (Exception e)
            {
                Logger.GetLogger().LogException(e);
            }
        }

        private static WeatherMonitorService instance;

        public static WeatherMonitorService GetInstance()
        {
            if (instance == null)
            {
                instance = new WeatherMonitorService();
            }
            return instance;
        }

        static WeatherZS GetWeatherZS(ref string html)
        {
            // refine this.
            string s2 = GetTextBetweenSpanTag(SpanPattern, ref html);
            string s3 = GetTextBetweenSpanTag(SpanPattern, ref html);
            string s4 = GetTextBetweenSpanTag(SpanPattern, ref html);
            string s5 = GetTextBetweenSpanTag(SpanPattern, ref html);
            string s6 = GetTextBetweenSpanTag(SpanPattern, ref html);
            string s7 = GetTextBetweenSpanTag(SpanPattern, ref html);
            string s8 = GetTextBetweenSpanTag(SpanPattern, ref html);
            string s9 = GetTextBetweenSpanTag(SpanPattern, ref html);
            string s10 = GetTextBetweenSpanTag(SpanPattern, ref html);
            string s11 = GetTextBetweenSpanTag(SpanPattern, ref html);

            WeatherZS zs = new WeatherZS();
            zs.Cloth = s3;
            zs.Cold = s4;
            zs.AirCondition = s5;
            zs.CarWash = s7;

            return zs;
        }

        static WeatherReport GetWeatherReport(string html)
        {
            WeatherReport report = new WeatherReport();
            int index = 0;

            index = html.IndexOf("<div class=\"day\"");
            html = html.Substring(index);

            report.InfoToday = GetWeatherInfo(ref html);
            report.InfoNight = GetWeatherInfo(ref html);

            report.InfoTomorrow = GetWeatherInfo2(ref html);
            report.InfoTomorrowNight = GetWeatherInfo2(ref html);

            return report;
        }

        static string GetTextBetweenSpanTag(string pattern, ref string html)
        {
            Match match = Regex.Match(html, pattern);
            int index = match.Index + match.Length;

            int index2 = html.IndexOf(SpanCloseTemplate, index);

            string result = html.Substring(index, index2 - index);
            html = html.Substring(index2 + SpanCloseTemplate.Length);

            return result.Trim();
        }

        static string GetTextBetweenLiTag(string LiOpenTag, ref string html)
        {
            int index = -1;
            int index2 = -1;
            index = html.IndexOf(LiOpenTag);
            index = index + LiOpenTag.Length;

            index2 = html.IndexOf(LiTemplateClose, index);
            string result = html.Substring(index, index2 - index);
            html = html.Substring(index2 + LiTemplateClose.Length);

            return result.Trim();
        }

        static WeatherInfo GetWeatherInfo2(ref string html)
        {
            WeatherInfo info = new WeatherInfo();

            int index = html.IndexOf(Mod03Template);
            html = html.Substring(index + Mod03Template.Length);

            info.Weather = GetTextBetweenLiTag(LiTemplate, ref html);
            info.Temperature = GetTextBetweenLiTag(LiTemplatePlus, ref html);
            info.Wind = GetTextBetweenLiTag(LiTemplate, ref html) + GetTextBetweenLiTag(LiTemplate, ref html);
            return info;
        }

        static WeatherInfo GetWeatherInfo(ref string html)
        {
            WeatherInfo info = new WeatherInfo();
            int index = 0;
            int index2 = 0;

            index = html.IndexOf(TemperatureTemplate) + TemperatureTemplate.Length;
            index2 = html.IndexOf(SpanCloseTemplate, index);

            info.Temperature = html.Substring(index, index2 - index).Trim();

            index = html.IndexOf(LiTemplate, index2 + SpanCloseTemplate.Length) + LiTemplate.Length;
            index2 = html.IndexOf(LiTemplateClose, index);

            info.Weather = html.Substring(index, index2 - index).Trim();

            index = html.IndexOf(LiTemplate, index2 + SpanCloseTemplate.Length) + LiTemplate.Length;
            index2 = html.IndexOf(LiTemplateClose, index);
            info.Wind = html.Substring(index, index2 - index).Trim();

            html = html.Substring(index2 + 6);
            return info;
        }

    }
}
